#include "list.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
/* Unit test define */
int test_count = 0, test_pass = 0, main_ret = 0;

#define EXPECT_EQ_BASE(equality, expect, actual, format) \
    do {\
        test_count++;\
        if (equality)\
            test_pass++;\
        else {\
            fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\
            main_ret = 1;\
        }\
    } while(0)

#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d")
#define EXPECT_EQ_POINTER(expect, actual) EXPECT_EQ_BASE(((void*)expect) == (void*)(actual), (void*)expect, (void*)actual, "%p")

/* End unit test define */

#define NODESIZE 10

volatile int i = 0;
list_t arr[NODESIZE] = {0};
uint32_t actSum = 0;
node_t nodeNoWarning;
node_t *node = &nodeNoWarning;
list_t listNoWarning = {.head = &nodeNoWarning};
list_t *list1 = &listNoWarning;
static list_t list2 = {0};
static node_t nodes[NODESIZE] = {0};

void createAdd_test(void)
{
    /* list static create test*/
    listStaticCreate(&list2);

    /* list create test */
    list1 = listCreate();
    if (list1 == NULL)
    {
        test_count += 2;
        printf("Malloc Failed\n");
        return;
    }

    /* node add test */
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        listStaticCreateNode(&nodes[i], &arr[i]);
        listInsertNodeAt(&list2, &nodes[i], i);
        node = listGetNode(&list2, i);
        if(NodeRead(node) == &arr[i] && listGetLen(&list2) == i+1)
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum);

    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        listPusht(list1, &arr[i]);
        node = listGetNode(list1, i);
        if(NodeRead(node) == &arr[i] && listGetLen(list1) == i+1)
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum);

    /* node move test */
    actSum = 0;
    node = listGetHead(list1);
    for (i = 0; i < listGetLen(list1); NodeToNext(&node),i++)
    {
        if (NodeRead(node) == &arr[i])
            actSum++;
    }
    EXPECT_EQ_INT(listGetLen(list1), actSum);
}

void search_test(void)
{
    /* Data search test*/
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        node = listSearchData(list1, &arr[i]);
        if (&arr[i] == NodeRead(node))
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum);

    /* Node search test*/
    actSum = 0;
    node = listGetHead(list1);
    for (i = 0; i < NODESIZE; i++)
    {
        if (listSearchNode(list1, node) == i)
            actSum++;
        node = NodeToNext(&node);
    }
    EXPECT_EQ_INT(NODESIZE, actSum);
}

void delete_test(void)
{
    /* Remove node test */
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        
    }
    
    /* Delete node test */
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        if(NodeRead(listGetNode(list1, 0)) == &arr[i] && (listGetLen(list1) == (NODESIZE - i)))
            actSum++;
        listDeleteNodeAt(list1, 0);
    }
    EXPECT_EQ_INT(NODESIZE, actSum);
}

void insert_test(void)
{
    /* test node insert */
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        listPush(list1, 0, &arr[i]);
    }
    while(i--)
    {
        if(listPoph(list1) == &arr[i])
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum + listGetLen(list1));

    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        node = listCreateNode(&arr[i]);
        listInsertNodeAt(list1, node, i);
    }
    while(i--)
    {
        if(listPopt(list1) == &arr[i])
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum + listGetLen(list1));

    /* test pop data*/
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        listPush(list1, i, &arr[i]);
    }
    while(i--)
    {
        if(listPop(list1, i) == &arr[i])
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum + listGetLen(list1));
}

void convert_test(void)
{
    /* test array to list*/
    actSum = 0;
    listArrayToList(list1, arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]));
    for (i = 0; i < NODESIZE; i++)
    {
        if(listPopt(list1) == &arr[(NODESIZE - 1) - i])
            actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum + listGetLen(list1));
}

void dataWR_test(void)
{
    for (i = 0; i < NODESIZE; i++)
    {
        listPusht(list1, &arr[i]);
    }

    while (listPopt(list1) == &arr[i-1])i--;
    EXPECT_EQ_INT(0, i);

    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        listPushh(list1, &arr[i]);
        listWrite(list1, i, &arr[i]);
        listRead(list1, i) == &arr[i] ? actSum++ : 0;
    }
    while (listPoph(list1));
    EXPECT_EQ_INT(NODESIZE, actSum);
}

void dataFree(void* memory)
{
    free(memory);
}

void dataFree_test(void)
{
    int *pdata = NULL;
    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        pdata = malloc(sizeof(int));
        listPushh(list1, pdata);
    }
    listDatasFree(list1, dataFree);
    listIteratort(list1, i){
        listPoph(list1) ? 0 : actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum);

    actSum = 0;
    for (i = 0; i < NODESIZE; i++)
    {
        pdata = malloc(sizeof(int));
        listPushh(list1, pdata);
    }
    listIteratort(list1, i){
        listDataFree(listGetNode(list1, i), dataFree);
    }
    listIteratort(list1, i){
        listPoph(list1) ? 0 : actSum++;
    }
    EXPECT_EQ_INT(NODESIZE, actSum);
}

void destory_test(void)
{
    listDestroy(&list1);
    EXPECT_EQ_POINTER(NULL, list1);
}

int main(void)
{
    int count = 1;
    while (count--)
    {
        createAdd_test();
        search_test();
        delete_test();
        insert_test();
        convert_test();
        dataWR_test();
        dataFree_test();
        destory_test();
        printf("result: %d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);
        //listCreate(); //Memory leak
        Sleep(500);
    }
    return main_ret;
}
